package com.uobgroup.ews.uam.web;

import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.util.UriUtils;
import org.springframework.web.util.WebUtils;

import com.uobgroup.ews.uam.core.domain.Reference;
import com.uobgroup.ews.uam.core.domain.ReferenceType;
import com.uobgroup.ews.uam.core.enums.ScopeType;
import com.uobgroup.ews.uam.core.service.CombinationMatrixService;
import com.uobgroup.ews.uam.core.service.ProfileService;
import com.uobgroup.ews.uam.core.service.ReferenceService;
import com.uobgroup.ews.uam.core.service.ReferenceTypeService;
import com.uobgroup.ews.uam.core.service.RoleSubTypeService;
import com.uobgroup.ews.uam.core.service.UAMRoleService;
import com.uobgroup.ews.uam.core.service.UAMUserService;
import com.uobgroup.ews.uam.web.validation.ReferenceValidator;

@RequestMapping("/references")
@Controller
public class ReferenceController {

   @Autowired
   private ReloadableResourceBundleMessageSource messageSource;
   
   @Autowired
   ReferenceService referenceService;
   
   @Autowired
   ReferenceTypeService referenceTypeService;
   
   @Autowired
   RoleSubTypeService roleSubTypeService;
   
   @Autowired
   UAMUserService userService; 
   
   @Autowired
   UAMRoleService roleService;
   
   @Autowired
   ProfileService profileService; 
   
   @Autowired
   CombinationMatrixService combinationMatrixService; 
   
   @Autowired
   ReferenceValidator referenceValidator;

   @InitBinder
   protected void InitBinder(WebDataBinder binder){
      binder.setValidator(referenceValidator);
   }

   @RequestMapping(method = RequestMethod.POST, produces = "text/html")
   public String create(@Valid Reference reference, BindingResult bindingResult, Model uiModel,
         HttpServletRequest httpServletRequest, Principal principal) {
      
      if (bindingResult.hasErrors()) {
         populateEditForm(uiModel, reference, httpServletRequest);
         return "references/create";
      }
      trimProperties(reference);
      uiModel.asMap().clear();
      try {
         reference.setLastModificationUserId(principal.getName());
         referenceService.saveReference(reference);
      } catch (Exception e) {
         uiModel.addAttribute("messageError", messageSource.getMessage("entity_not_create", new String[] {messageSource.getMessage("menu_category_reference_label", null, null)}, "", null));
         populateEditForm(uiModel, reference, httpServletRequest);
         return "references/create";
      }
      
      return "redirect:/references/view/" + encodeUrlPathSegment(reference.getId().toString(), httpServletRequest);
   }
   
   @RequestMapping(value = "/create", produces = "text/html")
   public String createForm(Model uiModel, HttpServletRequest httpServletRequest) {
      populateEditForm(uiModel, new Reference(), httpServletRequest);
      return "references/create";
   }

   @RequestMapping(value = "/view/{id}", produces = "text/html")
   public String show(@PathVariable("id") Long id, Model uiModel, HttpServletRequest httpServletRequest, final RedirectAttributes redirectAttrs) {
      Reference reference = null;
      if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_CHECKER")) {
         reference = referenceService.findReferenceByIdAndApplicationScope(id);
      } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_CHECKER")) {
         reference = referenceService.findReferenceByIdAndSecurityScope(id);
      }
      if (null == reference) {
         redirectAttrs.addFlashAttribute("error", messageSource.getMessage("entity_not_found_single", new String[] {messageSource.getMessage("menu_category_reference_label", null, null)}, "", null));
         redirectAttrs.addAttribute("page", "1");
         redirectAttrs.addAttribute("size", "10");
         return "redirect:/references";
      }
      uiModel.addAttribute("reference", reference);
      uiModel.addAttribute("itemId", id);
      return "references/show";
   }

   @RequestMapping(produces = "text/html")
   public String list(Reference reference, @RequestParam(value = "page", required = false) Integer page,
         @RequestParam(value = "size", required = false) Integer size, Model uiModel, HttpServletRequest httpServletRequest, @ModelAttribute("error") final String error) {
      int sizeNo = size == null ? 10 : size.intValue();
      final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;
      float nrOfPages = 0;
      Reference ref = new Reference();
      ReferenceType refType = reference.getReferenceType();
      List<Reference> refs = new ArrayList<Reference>();
      
      if (null != refType) {
         ref = referenceService.findReferencesByType(refType);
         refs = referenceService.findReferencesByType(refType, firstResult, sizeNo);
         long countReferencesByType = referenceService.countReferencesByType(refType);
         nrOfPages = (float) countReferencesByType / sizeNo;
         if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_CHECKER")) {
            if (ScopeType.SECURITY.equals(refType.getScope())) {
               ref = new Reference();
               refs = new ArrayList<Reference>();
            }
         } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_CHECKER")) {
            if (ScopeType.APPLICATION.equals(refType.getScope())) {
               ref = new Reference();
               refs = new ArrayList<Reference>();
            }
         }
         if (countReferencesByType <= 0) {
            ref = new Reference();
            if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_CHECKER")) {
               refs = referenceService.findReferencesThatHasApplicationScope(firstResult, sizeNo);
               nrOfPages = (float) referenceService.countReferencesByApplicationScope() / sizeNo;
            } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_CHECKER")) {
               refs = referenceService.findReferencesThatHasSecurityScope(firstResult, sizeNo);
               nrOfPages = (float) referenceService.countReferencesBySecurityScope() / sizeNo;
            }
         }
      }
      
      if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_CHECKER")) {
         if (null == refType) {
            refs = referenceService.findReferencesThatHasApplicationScope(firstResult, sizeNo);
            nrOfPages = (float) referenceService.countReferencesByApplicationScope() / sizeNo;
         }
         uiModel.addAttribute("referencetypes", referenceService.findReferenceTypesThatHasReferencesByApplicationScope());
         uiModel.addAttribute("references", refs);
      } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER") || httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_CHECKER")) {
         if (null == refType) {
            refs = referenceService.findReferencesThatHasSecurityScope(firstResult, sizeNo);
            nrOfPages = (float) referenceService.countReferencesBySecurityScope() / sizeNo;
         }
         uiModel.addAttribute("referencetypes", referenceService.findReferenceTypesThatHasReferencesBySecurityScope());
         uiModel.addAttribute("references", refs);
      }
      uiModel.addAttribute("maxPages", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1
         : nrOfPages));
      ReferenceType blankType = new ReferenceType();
      blankType.setId(new Long(0));
      uiModel.addAttribute("blankRefType", blankType);
      uiModel.addAttribute("reference", ref);
      uiModel.addAttribute("deleteMessageError", error);
      return "references/list";
   }

   @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
   public String update(@Valid Reference reference, BindingResult bindingResult, Model uiModel,
         HttpServletRequest httpServletRequest, Principal principal, final RedirectAttributes redirectAttrs) {
      Reference r1 = null;
      long id = reference.getId();
      if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER")) {
         r1 = referenceService.findReferenceByIdAndApplicationScope(id);
      } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER")) {
         r1 = referenceService.findReferenceByIdAndSecurityScope(id);
      }
      if (null == r1) {
         redirectAttrs.addFlashAttribute("error", messageSource.getMessage("entity_not_found_single", new String[] {messageSource.getMessage("menu_category_reference_label", null, null)}, "", null));
         redirectAttrs.addAttribute("page", "1");
         redirectAttrs.addAttribute("size", "10");
         return "redirect:/references";
      }
      if (bindingResult.hasErrors()) {
         populateEditForm(uiModel, reference, httpServletRequest);
         return "references/update";
      }
      
      trimProperties(reference);
      uiModel.asMap().clear();
      try {
         reference.setLastModificationUserId(principal.getName());
         referenceService.updateReference(reference);
      } catch (Exception e) {
         uiModel.addAttribute("messageError", messageSource.getMessage("entity_not_update", new String[] {messageSource.getMessage("menu_category_reference_label", null, null)}, "", null));
         populateEditForm(uiModel, reference, httpServletRequest);
         return "references/update";
      }
      return "redirect:/references/view/" + encodeUrlPathSegment(reference.getId().toString(), httpServletRequest);
   }

   @RequestMapping(value = "/update/{id}", produces = "text/html")
   public String updateForm(@PathVariable("id") Long id, Model uiModel, HttpServletRequest httpServletRequest, final RedirectAttributes redirectAttrs) {
      
      Reference reference = null;
      if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER")) {
         reference = referenceService.findReferenceByIdAndApplicationScope(id);
      } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER")) {
         reference = referenceService.findReferenceByIdAndSecurityScope(id);
      }
      if (null == reference) {
         redirectAttrs.addFlashAttribute("error", messageSource.getMessage("entity_not_found_single", new String[] {messageSource.getMessage("menu_category_reference_label", null, null)}, "", null));
         redirectAttrs.addAttribute("page", "1");
         redirectAttrs.addAttribute("size", "10");
         return "redirect:/references";
      }
      populateEditForm(uiModel, reference, httpServletRequest);
      return "references/update";
   }

   @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "text/html")
   public String delete(@PathVariable("id") Long id, @RequestParam(value = "page", required = false) Integer page,
         @RequestParam(value = "size", required = false) Integer size, @RequestParam(value = "referenceType", required = false) Integer referenceType, Model uiModel,HttpServletRequest httpServletRequest, Principal principal, final RedirectAttributes redirectAttrs) {
      Reference reference = null;
      if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER")) {
         reference = referenceService.findReferenceByIdAndApplicationScope(id);
      } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER")) {
         reference = referenceService.findReferenceByIdAndSecurityScope(id);
      }
      uiModel.asMap().clear();
      if (null == reference) {
         redirectAttrs.addFlashAttribute("error", messageSource.getMessage("entity_not_found_single", new String[] {messageSource.getMessage("menu_category_reference_label", null, null)}, "", null));
      } else {
         long hasRoleSubType = roleSubTypeService.countRoleSubTypeThatHasRoleTypeReference(reference);
         long hasRoleThatHasRoleTypeReference = roleService.countRoleThatHasRoleTypeReference(reference);
         long hasRoleThatHasProcessingCenterReference = roleService.countRoleThatHasProcessingCentreReference(reference);
         long hasRoleThatHasDepartmentReference = roleService.countRoleThatHasDepartmentReference(reference);
         
         long hasCbmThatHasCountryReference = combinationMatrixService.countCombinationMatrixsThatHasCountryReference(reference);
         long hasCbmThatHasDocumentTypeReference = combinationMatrixService.countCombinationMatrixsThatHasDocumentTypeReference(reference);
         long hasCbmThatHasProcessingCenterReference = combinationMatrixService.countCombinationMatrixsThatHasProcessingCenterReference(reference);
         long hasCbmThatHasProductTypeReference = combinationMatrixService.countCombinationMatrixsThatHasProductTypeReference(reference);
         long hasCbmThatHasTransactionTypeReference = combinationMatrixService.countCombinationMatrixsThatHasTransactionTypeReference(reference);
         long hasCbmThatHasUserLocationReference = combinationMatrixService.countCombinationMatrixsThatHasUserLocationReference(reference);
         
         long hasSegmentThatHasSegmentReference = referenceService.countSegmentsThatHasSegmentReference(reference);
         
         long hasProfileThatHasSegmentReference = profileService.countProfilesThatHasSegmentReference(reference);
         
         long hasUserThatHasCountryReference = userService.countUserThatHasCountryReference(reference);
         
         if (hasRoleSubType > 0 || hasRoleThatHasRoleTypeReference > 0 || hasRoleThatHasProcessingCenterReference > 0 
               || hasRoleThatHasDepartmentReference > 0 || hasCbmThatHasCountryReference > 0
               || hasCbmThatHasDocumentTypeReference > 0 || hasCbmThatHasProcessingCenterReference > 0
               || hasCbmThatHasProductTypeReference > 0 || hasCbmThatHasTransactionTypeReference > 0
               || hasCbmThatHasUserLocationReference > 0 || hasSegmentThatHasSegmentReference > 0
               || hasProfileThatHasSegmentReference > 0 || hasUserThatHasCountryReference > 0) {
            redirectAttrs.addFlashAttribute("error", messageSource.getMessage("field_cannot_delete", null, "", null));
         } else {
            try {
               reference.setLastModificationUserId(principal.getName());
               referenceService.deleteReference(reference);   
            } catch (Exception e) {
               redirectAttrs.addFlashAttribute("error", messageSource.getMessage("field_cannot_delete", null, "", null));
            }
         }
      }
      
      redirectAttrs.addAttribute("referenceType", (referenceType == null) ? 0 : referenceType);
      redirectAttrs.addAttribute("page", (page == null) ? 1 : page);
      redirectAttrs.addAttribute("size", (size == null) ? 10 : size);
      return "redirect:/references";
   }

   void populateEditForm(Model uiModel, Reference reference, HttpServletRequest httpServletRequest) {
      
      List<ReferenceType> referencetypes = new ArrayList<ReferenceType>();
      
      if (httpServletRequest.isUserInRole("ROLE_APPLICATION_PARAMETER_ADMINISTRATOR_MAKER")) {
         referencetypes = referenceTypeService.findReferenceTypesByApplicationScope();
      } else if (httpServletRequest.isUserInRole("ROLE_SECURITY_PARAMETER_ADMINISTRATOR_MAKER")) {
         referencetypes = referenceTypeService.findReferenceTypesBySecurityScope();
      }
      
      uiModel.addAttribute("reference", reference);
      uiModel.addAttribute("referencetypes", referencetypes);
      uiModel.addAttribute("segments", null);
   }

   String encodeUrlPathSegment(String pathSegment, HttpServletRequest httpServletRequest) {
      String enc = httpServletRequest.getCharacterEncoding();
      if (enc == null) {
         enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
      }
      try {
         pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
      }
      catch (UnsupportedEncodingException uee) {
      }
      return pathSegment;
   }

   void trimProperties(Reference reference) {
      reference.setCode(reference.getCode().trim());
      reference.setName(reference.getName().trim());
   }
}
